#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <utime.h>
#include <stdarg.h>
#include <time.h>

#include "fileutil.h"
#include "config.h"


#define stringcat(dest,src) strncat(dest, src, sizeof(dest) - 1 - strlen(dest))
#define stringcpy(dest,src) strncpy(dest, src, sizeof(dest) - 1)

int exists_file(const char *filename)
{
    struct stat st;

    if(stat(filename, &st) == 0)
        return 1;
	
    return 0;
}

int exists_dir(const char *filename)
{
    struct stat st;

    if(stat(filename, &st) != 0)
        return 0;
	
    if(!S_ISDIR(st.st_mode))
        return 0;

    return 1;
}

int log_printf(const char *format, ...)
{
    va_list vargs;
    FILE *pxllog;
    char output[4096];
    char timebuf[256];
    time_t now;
    struct tm *tm_now;
    
    pxllog = fopen(PXL_LOG,"a");
    if (!pxllog)
        return -1;

    memset(timebuf,0,256);
    memset(output,0,4096);

    time(&now); 
    tm_now = localtime(&now);

    strftime(timebuf,255,"[%Y/%m/%d-%H:%M:%S]",tm_now);
        
    va_start(vargs, format);
    vsnprintf(output,4095,format,vargs);
    va_end(vargs);
    fprintf(pxllog,"%s %s\n", timebuf, output);
    fclose(pxllog);
    
    return 0;
}

int empty_dir(const char *filename)
{
    struct stat st;
    DIR *checkdir;
    struct dirent *dp;
    int count;

    if(stat(filename, &st) != 0)
        return 0;
	
    if(!S_ISDIR(st.st_mode))
        return 0;

    checkdir = opendir(filename);
    if (!checkdir)
        return 0;
        
    count = 0;

    while (!count && ((dp = readdir(checkdir)) != NULL)) {
        if (dp->d_name && (strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))) {
            int templen;
            char *tempbuffer;
            
            templen = strlen(filename) + strlen(dp->d_name) + 3;
            tempbuffer = (char *)malloc(templen);
            memset(tempbuffer,0,templen);
            
            snprintf(tempbuffer,templen - 1,"%s/%s", filename, dp->d_name);
            
            if (exists_dir(tempbuffer)) {
                if (!empty_dir(tempbuffer))
                    count++;
            }
            else if (exists_file(tempbuffer)) {
                count++;
            }
            
            free(tempbuffer);
        }
    }
    closedir(checkdir);
    
    return (count == 0);
}


/* Make all directories along a given path if they don't exist */
int mkdirs(const char *path)
{
    char buf[MAXFILENAME];
    char cur[MAXFILENAME];
    char *c;

    /*
     * Okay, if the path doesn't end with a '/', assume the last element
     * is a filename, not a directory.
     */
    memset(buf, 0, sizeof(buf));
    memset(cur, 0, sizeof(cur));
    stringcpy(buf, path);

    c = strrchr(buf, '/');
    if(c)
      *c = '\0';

    /* correct for braindamage of strtok */
    if(*path == '/') stringcpy(cur, "/");
    
    c = strtok(buf, "/\\");
    while(c) {
        if (cur[0]) stringcat(cur, "/");
        stringcat(cur, c);
        if(exists_file(cur)) {
            /* Something with that name exists */
            if(!exists_dir(cur)) {
                /* It exists.. bail */
                return 0;
            } else {
                /* It exists and is a dir, continue */
                c = strtok(NULL, "/\\");
            }
        } else {
            /* Dir doesn't exist, so create it */
            mkdir(cur, 0775);
            c = strtok(NULL, "/\\");
        }
    }
    return 1;
}


void delete_dir(const char *path)
{
    DIR *workdir;
    struct dirent *dp;
    struct stat tstat;
    char *fullpath;
    int len;
    
    workdir = opendir(path);
    if (workdir) {
        while ((dp = readdir(workdir)) != NULL) {
            if (dp && dp->d_name && strcmp(dp->d_name,".") && strcmp(dp->d_name,"..")) {
               len = strlen(path) + strlen(dp->d_name) + 3;
               fullpath = (char *)malloc(len);
               memset(fullpath,0,len);
               snprintf(fullpath,len - 1,"%s/%s", path, dp->d_name);
    
               if (exists_dir(fullpath)) {
                   delete_dir(fullpath);
                   rmdir(fullpath);
               }
               else if (exists_file(fullpath)) {
                   unlink(fullpath);
               }
               free(fullpath);
            }
        }
        closedir(workdir);
        rmdir(path);
    }
}

int copy_file(const char *sourcepath, const char *destpath, int executable, int create_only)
{
    FILE *infile;
    FILE *outfile;
    size_t len;
    unsigned char readbuffer[1024];
    struct stat tstat;
    struct utimbuf acctime;

    mkdirs(destpath);

    if (exists_file(destpath)) {
        if (create_only)
            return 1;
    }

    infile = fopen(sourcepath,"rb");
    if (!infile) {
        return 0;
    }

    outfile = fopen(destpath,"wb");
    if (!outfile) {
        fclose(infile);
        return 0;
    }
    
    while (!feof(infile)) {
        len = fread(&readbuffer[0],sizeof(unsigned char),1024,infile);
        if (len) {
            fwrite(&readbuffer[0],sizeof(unsigned char),len,outfile);
        }
    }
    fclose(infile);
    fclose(outfile);

    stat(sourcepath, &tstat);

    if (executable == 1) {
        chmod(destpath, S_IRUSR | S_IWUSR | S_IXUSR |
                        S_IRGRP | S_IXGRP |
                        S_IROTH | S_IXOTH);
    }
    else if (executable == 0) {
        chmod(destpath, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
    }
    else {
        chmod(destpath, tstat.st_mode);
    }

    acctime.modtime = tstat.st_mtimespec.tv_sec;
    utime(destpath,&acctime);

    return 1;
}

int copy_dir(const char *sourcepath, const char *destpath, int create_only)
{
    DIR *sourcedir;
    struct dirent *dp;

    if (!exists_dir(sourcepath)) {
        return 0;
    }
    
    if (!exists_dir(destpath)) {
        char *tempbuf;
        int templen, tempres;
        
        templen = strlen(destpath) + 4;
        tempbuf = (char *)malloc(templen);
        memset(tempbuf, 0, templen);
        snprintf(tempbuf, templen - 1, "%s/.d", destpath);
        tempres = mkdirs(tempbuf);
        free(tempbuf);
        if (!tempres)
            return 0;
    }
    
    sourcedir = opendir(sourcepath);
    if (!sourcedir)
        return 0;
        
    while ((dp = readdir(sourcedir)) != NULL) {
        if (dp->d_name && (strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))) {
            char *sourcetemp, *desttemp;
            int sourcetemplen, desttemplen;
            int result;
            
            result = 0;
            
            sourcetemplen = strlen(sourcepath) + strlen(dp->d_name) + 3;
            sourcetemp = (char *)malloc(sourcetemplen);
            memset(sourcetemp,0,sourcetemplen);
            snprintf(sourcetemp,sourcetemplen - 1,"%s/%s", sourcepath, dp->d_name);

            desttemplen = strlen(destpath) + strlen(dp->d_name) + 3;
            desttemp = (char *)malloc(desttemplen);
            memset(desttemp,0,desttemplen);
            snprintf(desttemp,desttemplen - 1,"%s/%s", destpath, dp->d_name);
            
            if (exists_dir(sourcetemp)) {
                result = copy_dir(sourcetemp, desttemp, create_only);
            }
            else if (exists_file(sourcetemp)) {
                result = copy_file(sourcetemp, desttemp, -1, create_only);
            }

            free(sourcetemp);
            free(desttemp);
            
            if (!result) {
                closedir(sourcedir);
                return 0;
            }
        }
    }
    closedir(sourcedir);
    
    return 1;
}

